/***
 * * PROJECT
 * *    ATaG
 * * VERSION
 * *    $Revision: 1.14 $
 * * DATE
 * *    $Date: 2008/10/07 12:57:38 $
 * * LOG
 * *    $Log: ATaGProgram.java,v $
 * *    Revision 1.14  2008/10/07 12:57:38  animesh
 * *    removed GPL License [will re-add to all files later]
 * *
 * *    Revision 1.13  2007/11/20 22:44:56  animesh
 * *    no message
 * *
 * *    Revision 1.12  2007/08/09 01:58:59  animesh
 * *    getProducerTaskOfData added
 * *
 * *    Revision 1.11  2007/06/01 20:52:31  animesh
 * *    bulk update.
 * *
 * *    Revision 1.10  2007/01/29 16:27:08  lmottola
 * *    Fixed bug in casting arrays.
 * *
 * *    Revision 1.9  2007/01/29 08:06:22  animesh
 * *    tried casting the toArray of tempList, but without success.
 * *
 * *    Revision 1.8  2007/01/29 07:38:36  animesh
 * *    corrected one occurance of "sourceTask" to "targetTask".
 * *
 * *    Revision 1.7  2007/01/15 09:09:21  animesh
 * *    added function for returning scope used between T1 and T2 through D.
 * *    Also added functions to return task lists associated with a data item and vice versa.
 * *
 * *    Revision 1.6  2006/11/03 23:08:18  lmottola
 * *    Traffic application beta running.
 * *
 * *    Revision 1.5  2006/10/25 19:37:44  animesh
 * *    Moved functions from ATaGManager to this.
 * *
 * *    Revision 1.4  2006/10/21 00:17:54  animesh
 * *    no message
 * *
 * *    Revision 1.3  2006/10/19 21:21:53  lmottola
 * *    Added CVS preamble.
 * *
 * *
 * *   ATaG (Abstract Task Graph) - Data-driven Macroprogramming for
 * *                                Sensor Networks
 * *
 ***/

package atag.common;

import atag.compiler.*;
import atag.runtime.NodeInfo;
import atag.runtime.ln.ConjunctiveScope;
import atag.runtime.ln.LNScope;
import atag.runtime.ln.predicates.*;

import java.util.*;

/**
 * The main program node. This closely reflects the schema
 *
 * @author Animesh
 */
public class ATaGProgram {

    ArrayList<ATaGTaskDeclaration> taskList;
    ArrayList<ATaGDataItemDeclaration> dataList;
    ArrayList<ScopedChannel> channelList;

    /**
     * Creates a new instance of ATaGProgram
     */
    public ATaGProgram() {
        this.taskList = new ArrayList<ATaGTaskDeclaration>();
        this.dataList = new ArrayList<ATaGDataItemDeclaration>();
        this.channelList = new ArrayList<ScopedChannel>();

    }

    public ArrayList<ATaGTaskDeclaration> getTaskList() {
        return this.taskList;
    }

    public ArrayList<ATaGDataItemDeclaration> getDataList() {
        return this.dataList;
    }

    public ArrayList<ScopedChannel> getChannelList() {
        return this.channelList;
    }

    public int numTasks() {
        return this.taskList.size();
    }

    public int numDataItems() {
        return this.dataList.size();
    }

    public int numChannels() {
        return this.channelList.size();
    }

    /**
     * This method looks at the created task list and data list and channel list
     * and fills in the data that could not be filled by the parser
     * like the IDs etc.
     * This of this as pass 2 of the compiler
     */
    public void sanitize() {

        //check if all task names are unique

        for (int i = 0; i < this.taskList.size(); i++) {
            //set its ID
            this.taskList.get(i).setID(i);
            System.out.println("Task:"+this.taskList.get(i).getName()+"sets task with id="+i);

            for (int j = i + 1; j < this.taskList.size(); j++) {
                if (this.taskList.get(i).getName().equalsIgnoreCase(
                        this.taskList.get(j).getName())) {
                    //oops -- Names match
                    System.err.println("Both tasks " + i + " and " + j + " have " +
                            "the same name " + this.taskList.get(j).getName());
                    System.exit(-4);
                }
            }
        }// all tasks now have unique IDs also :-).

        //check if all data names are unique
        for (int i = 0; i < this.dataList.size(); i++) {
            //set its ID
            this.dataList.get(i).setID(i);
            System.out.println("DataItem:"+this.dataList.get(i).getName()+"sets data with id="+i);


            for (int j = i + 1; j < this.dataList.size(); j++) {
                if (this.dataList.get(i).getName().equalsIgnoreCase(
                        this.dataList.get(j).getName())) {
                    //oops -- Names match
                    System.err.println("Both tasks " + i + " and " + j + " have " +
                            "the same name " + this.dataList.get(j).getName());
                    System.exit(-4);
                }
            }
        }// all data items now have unique IDs also :-).

        //now all tasks and data items have unique IDs
        //for each channel.
        //set the taskID and dataID [make functions for them]

        for (ScopedChannel c : this.channelList) {

            if (c.isRPC()) {
                String taskNames[] = c.getTaskName().split(" ");
                int reqTaskID = this.taskIDFromName(taskNames[0]);
                int resTaskID = this.taskIDFromName(taskNames[1]);
                c.setTaskID(reqTaskID);
                System.out.println("RPCChannel:"+c.getID()+" sets tasks with ids= "+reqTaskID+", "+resTaskID);

            } else if (c.isRequestResponse()) {

                String taskNames[] = c.getTaskName().split(" ");
                int reqTaskID = this.taskIDFromName(taskNames[0]);
                int resTaskID = this.taskIDFromName(taskNames[1]);
                String dataNames[] = c.getDataName().split(" ");
                int reqdataID = this.dataIDFromName(dataNames[0]);
                int resdataID = this.dataIDFromName(dataNames[1]);

                c.setTaskID(reqTaskID);
                c.setDataID(reqdataID);
                System.out.println("RequestResponseChannel:"+c.getID()+" sets data with id="+c.getDataID());
                System.out.println("RequestResponseChannel:"+c.getID()+" sets task with id="+c.getTaskID());
                
                this.taskList.get(resTaskID).requestDataNames().add(dataNames[0]);
                this.taskList.get(resTaskID).responseDataNames().add(dataNames[1]);                

                this.taskList.get(reqTaskID).outDataNames().add(dataNames[0]);
                this.taskList.get(resTaskID).outDataNames().add(dataNames[1]);
                
                if(c.getInterest().equalsIgnoreCase("custom")) {
                    this.taskList.get(reqTaskID).customOutDataNames().add(dataNames[0]);
                    this.taskList.get(resTaskID).customOutDataNames().add(dataNames[1]);                        
                }
                
                this.taskList.get(reqTaskID).inDataNames().add(dataNames[1]);
                this.taskList.get(resTaskID).inDataNames().add(dataNames[0]);


            } else {

                int tID = this.taskIDFromName(c.getTaskName());
                int dID = this.dataIDFromName(c.getDataName());

                c.setTaskID(tID);
                c.setDataID(dID);
                System.out.println("Channel:"+c.getID()+" sets data with id="+c.getDataID());
                System.out.println("Channel:"+c.getID()+" sets task with id="+c.getTaskID());



                if (c.getDirection() == ScopedChannel.CHANNEL_TYPE_OUTPUT) {
                    //"output channel"
                    this.taskList.get(tID).outDataNames().add(c.getDataName());
                    if(c.getInterest().equalsIgnoreCase("custom")) {
                        this.taskList.get(tID).customOutDataNames().add(c.getDataName());
                    }
                } else {
                    //input channel
                    this.taskList.get(tID).inDataNames().add(c.getDataName());
                }
            }
        }// all channels done

        //make sure each data item has only one "producer"
        for (ATaGDataItemDeclaration d : this.dataList) {
            //for all channels
            boolean producerAlreadyFound = false;
            for (ScopedChannel c : this.channelList) {
                System.out.println("The channel is of type "+c.getDirection());
                if ((c.getDirection() == ScopedChannel.CHANNEL_TYPE_OUTPUT) &&
                        (c.getDataID() == d.getID())) {
                    if (producerAlreadyFound) {
                        //print error
                        System.err.println("Oops. Data item " + d.getName() +
                                "has two producers");
                        System.exit(-3);
                    } else {
                        producerAlreadyFound = true;
                    }
                }
            }
        }// for all data items
    }

    /**
     * returns the ID of a task given only its name
     */
    private int taskIDFromName(String name) throws NoSuchElementException {

        for (int i = 0; i < this.taskList.size(); i++) {
            if (this.taskList.get(i).getName().equalsIgnoreCase(name)) {
                return this.taskList.get(i).getID();
            }
        }
        // basically to cause an exception if the number is still -ve
        throw new NoSuchElementException("No task with name " + name + " found");
    }

    /**
     * returns the ID of a data given only its name
     */
    private int dataIDFromName(String name) throws NoSuchElementException {

        for (int i = 0; i < this.dataList.size(); i++) {
            if (this.dataList.get(i).getName().equalsIgnoreCase(name)) {
                return this.dataList.get(i).getID();
            }
        }
        // basically to cause an exception if the number is still -ve
        throw new NoSuchElementException("No data with name " + name + " found");
    }


    /**
     * @param dataID the ID of the data
     * @returns an array of channelIDs which are the input channels attached to a data item
     */
    public ScopedChannel[] inputChannelsD(int dataID) {
        int count = 0;
        for (int i = 0; i < channelList.size(); i++)
            if ((channelList.get(i).getDataID() == dataID) &&
                    (channelList.get(i).getTypeInString().toUpperCase()).equals("INPUT"))
                count++;
        ScopedChannel[] returnVal = new ScopedChannel[count];
        int j = 0;
        for (int i = 0; i < channelList.size(); i++)
            if ((channelList.get(i).getDataID() == dataID) &&
                    (channelList.get(i).getTypeInString().toUpperCase()).equals("INPUT"))
                returnVal[j++] = channelList.get(i);

        return returnVal;
    }

    /**
     * @param dataID the ID of the data
     * @returns an array of channels which are the input
     * channels attached to a data item
     */
    public ArrayList<ScopedChannel> getInChannelListForData(int dataID) {
        ArrayList<ScopedChannel> retList = new ArrayList();
        for (int i = 0; i < channelList.size(); i++)
            if ((channelList.get(i).getDataID() == dataID) &&
                    (channelList.get(i).getTypeInString().toUpperCase()).equals("INPUT")) {
                retList.add(channelList.get(i));
            }
        return retList;
    }


    /**
     * @param dataID the ID of the data
     * @returns an array of channelIDs which are the output channels attached to a data item
     */
    public ScopedChannel[] outputChannelsD(int dataID) {
        int count = 0;
        for (int i = 0; i < channelList.size(); i++)
            if ((channelList.get(i).getDataID() == dataID) &&
                    (channelList.get(i).getTypeInString().toUpperCase()).equals("OUTPUT"))
                count++;
        ScopedChannel[] returnVal = new ScopedChannel[count];
        int j = 0;
        for (int i = 0; i < channelList.size(); i++)
            if ((channelList.get(i).getDataID() == dataID) &&
                    (channelList.get(i).getTypeInString().toUpperCase()).equals("OUTPUT"))
                returnVal[j++] = channelList.get(i);

        return returnVal;
    }

    /**
     * @param taskID the ID of the task
     * @param dataID the ID of the data
     * @returns the channel, given the task ID and the data ID
     */
    public ScopedChannel getChannelFromTaskAndData(int taskID, int dataID)
            throws NoSuchElementException {
        boolean found = false;
        ScopedChannel retChannel = null;
        for (ScopedChannel c : this.channelList) {
            if (c.isRequestResponse()) {
                String taskNames[] = c.getTaskName().split(" ");
                int reqTaskID = this.taskIDFromName(taskNames[0]);
                int resTaskID = this.taskIDFromName(taskNames[1]);
                String dataNames[] = c.getDataName().split(" ");
                int reqdataID = this.dataIDFromName(dataNames[0]);
                int resdataID = this.dataIDFromName(dataNames[1]);

                if (reqTaskID == taskID || resTaskID == taskID) {
                    if (reqdataID == dataID || resdataID == dataID) {
                        if (found == true) {// this is a duplicate
                            System.err.println("There are two channels with the same Data ID " + dataID
                                    + " and Task " + taskID);
                            System.exit(-4);
                        } else {// first occurance
                            retChannel = c;
                            found = true;
                        }
                    }
                }
            } else {
                if ((c.getDataID() == dataID) && (c.getTaskID() == taskID)) {
                    if (found == true) {// this is a duplicate
                        System.err.println("There are two channels with the same Data ID " + dataID
                                + " and Task " + taskID);
                        System.exit(-4);
                    } else {// first occurance
                        retChannel = c;
                        found = true;
                    }
                }
            }
        }
        if (found == false) //still not found
            throw new NoSuchElementException("No channel connecting Data ID " + dataID
                    + " and Task " + taskID + " found");
        else
            return retChannel;


    }

    public int[] getInDataOfTask(int taskID) {
        // go through channels and get a list
        int x = this.numDataItems();
        int[] dataArray = new int[x];
        //TODO -- not inited to zeros
        for (ScopedChannel t_channelDecl : this.channelList) {
            // If the input channel matches this task ID,
            // Mark the channel's data ID index as true;
            if (t_channelDecl.isInput() && (t_channelDecl.taskID() == taskID))
                dataArray[t_channelDecl.dataID()] = 1;
        }
        return dataArray;
    }

    public ArrayList<ATaGDataItemDeclaration> getInDataListOfTask(int taskID) {
        // go through channels and get a list
        ArrayList dataArray = new ArrayList<ATaGDataItemDeclaration>();
        for (ScopedChannel t_channelDecl : this.channelList) {
            // If the input channel matches this task ID,
            // add the dataItem to the list
            if (t_channelDecl.isInput() && (t_channelDecl.taskID() == taskID))
                dataArray.add(this.dataList.get(t_channelDecl.dataID()));
        }
        return dataArray;
    }

    public ArrayList<ATaGDataItemDeclaration> getOutDataListOfTask(int taskID) {
        // go through channels and get a list
        ArrayList dataArray = new ArrayList<ATaGDataItemDeclaration>();
        for (ScopedChannel t_channelDecl : this.channelList) {
            // If the input channel matches this task ID,
            // Mark the channel's data ID index as true;
            if (t_channelDecl.isOutput() && (t_channelDecl.taskID() == taskID))
                dataArray.add(this.dataList.get(t_channelDecl.dataID()));
        }
        return dataArray;
    }


    public int[] getOutTasksOfData(int dataID) {
        // go through channels and get a list
        int x = this.numTasks();
        int[] taskArray = new int[x];
        //TODO -- not inited to zeros
        for (ScopedChannel t_channelDecl : this.channelList) {
            // If the input channel source matches this data ID,
            // Mark the input channel's task ID index as true;
            if (t_channelDecl.isInput() && (t_channelDecl.dataID() == dataID))
                taskArray[t_channelDecl.taskID()] = 1;
        }
        return taskArray;
    }

    /**
     * @returns The producer of the data item
     */
    public ATaGTaskDeclaration getProducerOfData(int dataID) {
        ATaGTaskDeclaration retTask = null;
        for (ScopedChannel t_channelDecl : this.channelList) {
            // If the output channel source matches this data ID,
            if (t_channelDecl.isOutput() && (t_channelDecl.dataID() == dataID))
                //set retTask to that task
                retTask = this.taskList.get(t_channelDecl.taskID());
            else if (t_channelDecl.isRequestResponse()) {
                String taskNames[] = t_channelDecl.getTaskName().split(" ");
                int reqTaskID = this.taskIDFromName(taskNames[0]);
                int resTaskID = this.taskIDFromName(taskNames[1]);
                String dataNames[] = t_channelDecl.getDataName().split(" ");
                int reqdataID = this.dataIDFromName(dataNames[0]);
                int resdataID = this.dataIDFromName(dataNames[1]);
                if (reqdataID == dataID) {
                    retTask = this.taskList.get(reqTaskID);
                } else if (resdataID == dataID) {
                    retTask = this.taskList.get(resTaskID);
                }

            }
        }
        return retTask;
    }

    /**
     * @returns List of tasks that consume the dataItem
     */
    public ArrayList<ATaGTaskDeclaration> getOutTasksListOfData(int dataID) {
        // go through channels and get a list
        ArrayList taskArray = new ArrayList<ATaGTaskDeclaration>();
        for (ScopedChannel t_channelDecl : this.channelList) {
            // If the input channel source matches this data ID,
            // Mark the input channel's task ID index as true;
            if (t_channelDecl.isInput() && (t_channelDecl.dataID() == dataID))
                //insert relevant task to the array
                taskArray.add(this.taskList.get(t_channelDecl.taskID()));
            else if (t_channelDecl.isRequestResponse()) {
                String taskNames[] = t_channelDecl.getTaskName().split(" ");
                int reqTaskID = this.taskIDFromName(taskNames[0]);
                int resTaskID = this.taskIDFromName(taskNames[1]);
                String dataNames[] = t_channelDecl.getDataName().split(" ");
                int reqdataID = this.dataIDFromName(dataNames[0]);
                int resdataID = this.dataIDFromName(dataNames[1]);
                if (reqdataID == dataID) {
                    taskArray.add(this.taskList.get(resTaskID));
                } else if (resdataID == dataID) {
                    taskArray.add(this.taskList.get(reqTaskID));
                }

            }
        }
        return taskArray;
    }

    /**
     * @param taskID     the ID of task that produces the data
     * @param dataID     the ID of the data item
     * @param nInfo      the nodeInfo for the node asking for the information
     * @param targetTask is the targetTask
     * @returns an LNScope, representing the output channel from source to destination
     */
    public LNScope getLNScopeForData(int taskID, int dataID, NodeInfo nInfo,
                                     ATaGTaskDeclaration targetTask) {
        ATaGDataItemDeclaration d = this.dataList.get(dataID);

        ATaGTaskDeclaration sourceTask = this.taskList.get(taskID);
        ScopedChannel outChannel = this.getChannelFromTaskAndData(taskID, dataID);


        ArrayList<LNPredicate> tempList = new ArrayList<LNPredicate>();
        // first add condition that target is running particular task
        LNPredicate targetTaskInstantiated = new SetMembershipPredicate(
                new Integer(targetTask.getID()),
                SetMembershipPredicate.IS_IN,
                NodeInfo.ASSIGNED_TASK_ATTR_NAME);
        tempList.add(targetTaskInstantiated);

        // then add the condition obtained from output channel
        String outputInterest = outChannel.getInterest();
        //the if below should be a case/switch ideally - Animesh
        if (outputInterest.equalsIgnoreCase("none")) {
            //do nothing. Nothing to add
        } else if (outputInterest.equalsIgnoreCase("region-hops")) {
            //create a hop thing
            String regionLabel = outChannel.getInterestLabel();
            int regionRange = Integer.parseInt(outChannel.getInterestParam());
            int myRegionID =
                    ((Integer) nInfo.getAttributeByName(regionLabel)).intValue();

            LNPredicate regionHopPredicate = new IntegerRangePredicate(regionLabel,
                    myRegionID - regionRange, myRegionID + regionRange);

            tempList.add(regionHopPredicate);

        } else if (outputInterest.equalsIgnoreCase("domain")) {
            //handle domain
            int instType = sourceTask.getLocation()[0];

            switch (instType) {
                case ATaGTaskDeclaration.INSTANTIATION_PARTITION_PER_INSTANCE:
                    // if task is "partition-per-instance, get partition name"
                    String regionLabel = sourceTask.getMyRegionName();
                    int myRegionID =
                            ((Integer) nInfo.getAttributeByName(regionLabel)).intValue();

                    LNPredicate domainPredicate = new IntegerSimplePredicate(regionLabel,
                            IntegerSimplePredicate.EQUAL, myRegionID);
                    tempList.add(domainPredicate);
                    break;
                default:
                    //fail
                    System.err.println("Task " + sourceTask.getName() + "is attached" +
                            "to a domain channel but has incompatible instantiation type"
                            + sourceTask.getInstType());
                    System.exit(-1);
            }//end switch
        }// end domain stuff

        // then add the condition obtained from input channel
        //TODO -- the code below is repeated. Make a function out of it
        ScopedChannel inChannel = this.getChannelFromTaskAndData(targetTask.getID(), dataID);
        String inputInterest = inChannel.getInterest();
        //the if below should be a case/switch ideally - Animesh
        if (inputInterest.equalsIgnoreCase("none")) {
            //do nothing. Nothing to add
        } else if (inputInterest.equalsIgnoreCase("region-hops")) {
            //create a hop thing
            String regionLabel = inChannel.getInterestLabel();
            int regionRange = Integer.parseInt(inChannel.getInterestParam());
            int myRegionID =
                    ((Integer) nInfo.getAttributeByName(regionLabel)).intValue();

            LNPredicate regionHopPredicate = new IntegerRangePredicate(regionLabel,
                    myRegionID - regionRange, myRegionID + regionRange);

            tempList.add(regionHopPredicate);
        } else if (inputInterest.equalsIgnoreCase("domain")) {
            //handle domain
            int instType = targetTask.getLocation()[0];

            switch (instType) {
                case ATaGTaskDeclaration.INSTANTIATION_PARTITION_PER_INSTANCE:
                    String regionLabel = targetTask.getMyRegionName();
                    // if task is "partition-per-instance, get partition name"
                    int myRegionID =
                            ((Integer) nInfo.getAttributeByName(regionLabel)).intValue();

                    LNPredicate domainPredicate = new IntegerSimplePredicate(regionLabel,
                            IntegerSimplePredicate.EQUAL, myRegionID);
                    tempList.add(domainPredicate);
                    break;
                default:
                    //fail
                    System.err.println("Task " + targetTask.getName() + "is attached" +
                            "to a domain channel but has incompatible instantiation type"
                            + sourceTask.getInstType());
                    System.exit(-1);
            }//end switch
        }// end domain stuff

        // finally, convert to LNScope[] and feed to conjunctivescope
        LNPredicate[] retPredicate = (tempList.toArray(new LNPredicate[]{}));

        // insert the conjunctivescope to the bigger array
        LNScope retScope = new ConjunctiveScope(retPredicate);

        return retScope;
    }

    /**
     * @param at Abstract Task
     * @returns List of tasks that "feed" data into at
     */
    public ArrayList<ATaGTaskDeclaration> getFeederTasks(ATaGTaskDeclaration at) {
        //TODO -- improve
        //simple method - do exhaustive search over all tasks - O(n)
        //more complex method, have reverse pointers - O(k)
        //

        ArrayList<ATaGTaskDeclaration> retArr = new ArrayList();

        for (ATaGTaskDeclaration testTask : this.taskList) {
            //for each data produced by this task
            for (ATaGDataItemDeclaration testData :
                    getOutDataListOfTask(testTask.getID())) {
                //for each task consuming this data item
                for (ATaGTaskDeclaration testTask2 :
                        this.getOutTasksListOfData(testData.getID())) {
                    //if at is a "consumer" of this data item
                    if (testTask2.getID() == at.getID()) {
                        //add to list. Don't look at other tasks
                        retArr.add(testTask);
                        break;
                    }
                }
            }
        }
        return retArr;
    }


    /**
     * @param taskID     the ID of task that produces the data
     * @param dataID     the ID of the data item
     * @param targetTask is the targetTask
     * @returns an LNScope, representing the output channel from source to destination
     */
    public LNScope getLNScopeEdgeForCATaG(int sourceTaskID, int dataID,
                                          ATaGTaskDeclaration targetTask) {

        ATaGDataItemDeclaration d = this.dataList.get(dataID);

        ATaGTaskDeclaration sourceTask = this.taskList.get(sourceTaskID);
        ScopedChannel outChannel = this.getChannelFromTaskAndData(sourceTaskID, dataID);


        ArrayList<LNPredicate> tempList = new ArrayList<LNPredicate>();
        // first add condition that target is running particular task
        /* removed since this is not needed for edges in CATaG
        LNPredicate targetTaskInstantiated = new SetMembershipPredicate(
                new Integer(targetTask.getID()),
                SetMembershipPredicate.IS_IN,
                NodeInfo.ASSIGNED_TASK_ATTR_NAME);
        tempList.add(targetTaskInstantiated);
        */

        // then add the condition obtained from output channel
        String outputInterest = outChannel.getInterest();
        //the if below should be a case/switch ideally - Animesh
        if (outputInterest.equalsIgnoreCase("none")) {
            //do nothing. Nothing to add
        } else if (outputInterest.equalsIgnoreCase("custom")) {
            //do nothing. Nothing to add
        } else if (outputInterest.equalsIgnoreCase("region-hops")) {
            //create a hop thing
            String regionLabel = outChannel.getInterestLabel();
            int regionRange = Integer.parseInt(outChannel.getInterestParam());

            //note that we are setting the regionID to 0 - this is a special ID
            int myRegionID = 0;

            LNPredicate regionHopPredicate = new IntegerRangePredicate(regionLabel,
                    myRegionID - regionRange, myRegionID + regionRange);

            tempList.add(regionHopPredicate);

        } else if (outputInterest.equalsIgnoreCase("domain")) {
            //handle domain
            int instType = sourceTask.getLocation()[0];

            switch (instType) {
                case ATaGTaskDeclaration.INSTANTIATION_PARTITION_PER_INSTANCE:
                    // if task is "partition-per-instance, get partition name"
                    String regionLabel = sourceTask.getMyRegionName();
                    //note that we are setting the regionID to 0 - this is a special ID
                    int myRegionID = 0;

                    LNPredicate domainPredicate = new IntegerSimplePredicate(regionLabel,
                            IntegerSimplePredicate.EQUAL, myRegionID);
                    tempList.add(domainPredicate);
                    break;
                default:
                    //fail
                    System.err.println("Task " + sourceTask.getName() + "is attached" +
                            "to a domain channel but has incompatible instantiation type"
                            + sourceTask.getInstType());
                    System.exit(-1);
            }//end switch
        }// end domain stuff

        // then add the condition obtained from input channel
        //TODO -- the code below is repeated. Make a function out of it
        ScopedChannel inChannel = this.getChannelFromTaskAndData(targetTask.getID(), dataID);
        String inputInterest = inChannel.getInterest();
        //the if below should be a case/switch ideally - Animesh
        if (inputInterest.equalsIgnoreCase("none")) {
            //do nothing. Nothing to add
        } else if (outputInterest.equalsIgnoreCase("custom")) {
            //do nothing. Nothing to add
        } else if (inputInterest.equalsIgnoreCase("region-hops")) {
            //create a hop thing
            String regionLabel = inChannel.getInterestLabel();
            int regionRange = Integer.parseInt(inChannel.getInterestParam());
            //note that we are setting the regionID to 0 - this is a special ID
            int myRegionID = 0;

            LNPredicate regionHopPredicate = new IntegerRangePredicate(regionLabel,
                    myRegionID - regionRange, myRegionID + regionRange);

            tempList.add(regionHopPredicate);
        } else if (inputInterest.equalsIgnoreCase("domain")) {
            //handle domain
            int instType = targetTask.getLocation()[0];

            switch (instType) {
                case ATaGTaskDeclaration.INSTANTIATION_PARTITION_PER_INSTANCE:
                    String regionLabel = targetTask.getMyRegionName();
                    // if task is "partition-per-instance, get partition name"
                    //note that we are setting the regionID to 0 - this is a special ID
                    int myRegionID = 0;

                    LNPredicate domainPredicate = new IntegerSimplePredicate(regionLabel,
                            IntegerSimplePredicate.EQUAL, myRegionID);
                    tempList.add(domainPredicate);
                    break;
                default:
                    //fail
                    System.err.println("Task " + targetTask.getName() + "is attached" +
                            "to a domain channel but has incompatible instantiation type"
                            + sourceTask.getInstType());
                    System.exit(-1);
            }//end switch
        }// end domain stuff

        // finally, convert to LNScope[] and feed to conjunctivescope
        LNPredicate[] retPredicate = (tempList.toArray(new LNPredicate[]{}));

        // insert the conjunctivescope to the bigger array
        LNScope retScope = new ConjunctiveScope(retPredicate);

        return retScope;
    }

    public ATaGTaskDeclaration getRPCServer(int clientTaskID) {
        for(ScopedChannel sc: channelList)
        {
            if(sc.isRPC())
            {
                String tasks[]=sc.getTaskName().split(" ");
                if(taskIDFromName(tasks[0])==clientTaskID)
                {
                    return taskList.get(taskIDFromName(tasks[1]));
                }                
            }
        }
        return null;
    }

    /**
     * Scrubs each task's assignedTo list, and returns the object
     * @return
     */
    @Deprecated
	public ATaGProgram scrubAssignments() {
		
		for(ATaGTaskDeclaration t: this.taskList){
			t.scrubAssignment();
		}
		
		
		return this;
	}
}
